#include "../include/defs.h"
#include "../include/kc.h"
#include "../include/sleep.h"
#include "../include/video.h"

#define STACK_USUARIOS  0x1000
#define DEF_PRIORITY    3

extern process_t procesos[];
extern peticion peticionesDormir[];
extern TTY terminales[];

extern int terminalActual;
extern int pidActual;
extern int proxPid;

extern unsigned long *page_directory;
extern unsigned long *page_table;


void sleep(int time) {
	int i, j;

	if (time <= 0)
		return;

	for (i = 0; i < MAXPROCESOS; i++) {
		if (peticionesDormir[i].time == 0) {
			/*Agrego una entrada al array de procesos
			 que quieren dormir */
			peticionesDormir[i].pid = pidActual;

			peticionesDormir[i].time = time * TIMECONSTANT;

			/*Tengo que buscar proceso para luego bloquearlo */
			bloqueaProceso(pidActual, ESPERANDO_SLEEP);
			switch_manual();

			return;
		}
	}
}
int ProcesoExiste(int pid) {
	int i;

	if (pid == 0)
		return 1;

	for (i = 0; i < MAXPROCESOS; i++) {
		if (procesos[i].pid == pid && !procesos[i].free_slot)
			return 1;
	}

	return 0;

}

process_t *
GetProcessByPid(int pid) {
	int i;

	for (i = 0; i < MAXPROCESOS; i++) {
		if (procesos[i].pid == pid && !procesos[i].free_slot)
			return &procesos[i];
	}

	return 0;
}

process_t*
GetProcessByPosition(int i) {
	return &procesos[i];
}

process_t *
GetProcessByName(char *proceso) {
	int i;
	int len1, len2;

	len1 = strlen(proceso);

	for (i = 0; i < MAXPROCESOS; i++) {
		len2 = strlen(procesos[i].nombre);

		if (strncmp(proceso, procesos[i].nombre, len1) == 0 && len1 == len2
				&& !procesos[i].free_slot) {
			return &procesos[i];
		}
	}

	return 0;
}

int NoHayProcesos(void) {
	int i;

	for (i = 0; i < MAXPROCESOS; i++) {
		if (i == IDLE)
			continue;
		if (procesos[i].free_slot == 0 && !procesos[i].bloqueado)
			return 0;
	}
	return 1;
}

int GetPid(void) {
	return proxPid++;
}

void Limpia(void) {
	/* deberia matar al proceso y toda la bola */
	process_t *temporal;
	process_t *padre;

	temporal = GetProcessByPid(pidActual);

	if (temporal->padre != IDLE) {
		padre = GetProcessByPid(temporal->padre);
		padre->bloqueado = 0;
	}
	temporal->free_slot = 1;
	while (1) {
		asm volatile ("hlt");
	}
}

void exec(char *nombre, int(*proceso)(int argc, char **argv), int enBackground) {
	CrearProceso(nombre, proceso, terminalActual, 0, (char **) 0, DEF_PRIORITY,
			enBackground, STACK_USUARIOS);
}

void CrearProceso(char *nombre, int(*proceso)(int argc, char **argv), int tty,
		int argc, char **argv, int prioridad, int enBackground, int stacksize) {
	int i;

	void *stack;
	void *heap;


	stack = Malloc(stacksize);
	heap = Malloc(HEAP_SIZE);

/*	stack = mm_malloc(stacksize);
	heap =  mm_malloc(HEAP_SIZE);*/

	for (i = 1; i < MAXPROCESOS; i++) {
		if (procesos[i].free_slot)
			break;
	}

	procesos[i].pid = GetPid();
	procesos[i].background = enBackground;
	procesos[i].prioridad = prioridad;
	strncpy(procesos[i].nombre, nombre, strlen(nombre) + 1);
	procesos[i].bloqueado = 0;
	procesos[i].terminal = tty;
	procesos[i].stacksize = stacksize;
	procesos[i].stackstart = (int) stack + stacksize - 1;
	procesos[i].heapsize = HEAP_SIZE;
	procesos[i].heapstart = (int) heap;
	procesos[i].nextfree = (int) heap;
	/*Levanto las paginas de este proceso para poder armar stack*/
	levantaPaginas((process_t *) &procesos[i]);

	procesos[i].ESP = ArmaStackFrame(proceso, procesos[i].stackstart, Limpia);

	/*Bajo las paginas para evitar que cualquier otro proceso escriba
	 en la zona de este*/
	bajarPaginas((process_t*) &procesos[i]);

	procesos[i].padre = pidActual;

	if (!enBackground && pidActual > 0) {
		process_t *proc = GetProcessByPid(pidActual);
		/* bloqueado esperando que termine su hijo */
		proc->bloqueado = ESPERANDO_HIJO;
		procesos[i].padre = pidActual;
	}
	procesos[i].free_slot = 0;

}

void Kill(int pid) {
	process_t * proc;
	process_t * padre;
	if (pid != IDLE) {
		proc = GetProcessByPid(pid);
		if (proc != 0) {
			printf("El proceso ", 11);
			printf(proc->nombre, strlen(proc->nombre));
			printf(" fue eliminado.\n", 16);
			proc->free_slot = 1;
			proc->bloqueado = 0;
			proc->pid = -1;
			matarHijos(pid);
			if (proc->padre != 0) {
				padre = GetProcessByPid(proc->padre);
				if (proc->padre != IDLE) {
					if (padre->padre != 0 && !proc->background)
						desbloqueaProceso(proc->padre);
					else if (padre->terminal == terminalActual
							&& !proc->background)
						desbloqueaProceso(proc->padre);
					else {
						if (!proc->background) {
							desbloqueaProceso(proc->padre);
							bloqueaProceso(proc->padre, 1);
						}
					}
				}
			} else if (proc->padre != IDLE)
				desbloqueaProceso(proc->padre);
		} else
			printf("Proceso no encontrado.\n", 23);
	} else {
		printf("Proceso protegido.\n", 19);
	}
	return;
}
/*void Kill(int pid) {
};*/
int Nada(int argc, char **argv) {
	while (1) {
		asm volatile ("hlt");
	}
}

int procesosex[MAXPROCESOS];

void matarHijos(int pidPadre) {

	int i;
	for (i = 0; i < MAXPROCESOS; i++) {
		/*Solo mata los hijos que no estan en background */
		if (procesos[i].padre == pidPadre && !procesos[i].background) {
			procesos[i].free_slot = 0;
			Kill(procesos[i].pid);
		}
	}
	return;
}
/*
 * 	Cambia la prioridad del proceso
 *
 */
void Renice(int pid, int prioridad) {
	process_t *proc = GetProcessByPid(pid);
	if (proc != 0) {
		if (prioridad >= 0 && prioridad < CANT_PRIOR) {
			proc->prioridad = prioridad;
			print("Prioridad del proceso ");
			print(proc->nombre);
			print(" cambiada.\n");
		} else
			print("Prioridad Invalida.\n");

	} else {
		print("Proceso no encontrado.\n");
	}
}
